﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Threading;
using VideoReflector.Models;
using BuzzIO;

namespace VideoReflector.Controllers
{
    /// <summary>
    /// Controller tasked with creation of tags (either via UI command or Buzz device controller presses)
    /// Records elapsed time internally and should have Start / Stop methods called at same time as
    /// corresponding calls to IVideoRecorder object
    /// </summary>
    public class TagController : NotifyingObject, ITagController
    {
        #region Private fields

        private ICollection<ClipTag> _tags;
        private int _clipLength;
        private DispatcherTimer _timer;
        private bool _enabled;
        private int _elapsedTime;
        private Dispatcher _dispatcher;

        #endregion // Private fields

        #region Constructor

        /// <summary>
        /// Constructor for TagController object
        /// </summary>
        /// <param name="tags"> Collection of session ClipTags which generated tags should be added to </param>
        /// <param name="clipLength"> Length of clips in seconds to use with new tags </param>
        /// <param name="dispatcher"> Dispatcher object to queue tag creation calls on </param>
        public TagController(ICollection<ClipTag> tags, int clipLength, Dispatcher dispatcher)
        {
            _tags = tags;
            _clipLength = clipLength;
            _dispatcher = dispatcher;
            _elapsedTime = 0; 
        }

        #endregion // Constructor

        #region Event handlers

        /// <summary>
        /// Elapsed time update handler
        /// </summary>
        private void _timer_Tick(object sender, EventArgs e)
        {
            _elapsedTime++;
            NotifyPropertyChanged("ElapsedTime");
        }

        /// <summary>
        /// Buzz controller button press handler
        /// </summary>
        void buzzDevice_OnButtonChanged(object sender, BuzzButtonChangedEventArgs args)
        {
            _dispatcher.BeginInvoke(new Action( () =>
                {
                    // Iterate over each handset's buttons
                    for (int i = 0; i < 4; i++)
                    {
                        // if handset red (large) button was pressed and can currently add tags
                        if (args.Buttons[i].Red & CanAddTag(i))
                        {
                            // add a tag corresponding to the handset index
                            AddTag(i);
                            // temporarily disable adding tags to prevent registering accidental multiple presses
                            Enabled = false;
                            // turn light of relevant handset on
                            ((BuzzHandsetDevice)sender).SetLights(i == 0, i == 1, i == 2, i == 3);
                            // create a new dispatcher timer to reset lights and enabled state
                            var timer = new DispatcherTimer(DispatcherPriority.Normal, _dispatcher);
                            // state will reset after two seconds
                            timer.Interval = new TimeSpan(0, 0, 2);
                            timer.Tick += (s, e) =>
                            {
                                Enabled = true;
                                ((BuzzHandsetDevice)sender).SetLights(false, false, false, false);
                                timer.Stop();
                            };
                            timer.Start();
                        }
                    }
                }
            ));
        }

        #endregion // Event handlers

        #region Public properties

        /// <summary>
        /// Whether the controller is current enabled for addition of tags via the AddTag command using
        /// the internal timer for generating the tag timestamps
        /// </summary>
        public bool Enabled
        {
            get 
            { 
                return _enabled; 
            }
            private set 
            { 
                _enabled = value; 
                NotifyPropertyChanged("Enabled"); 
            }
        }

        /// <summary>
        /// The current elapsed time in seconds of the internal tag timer
        /// </summary>
        public int ElapsedTime
        {
            get
            {
                return _elapsedTime;
            }
        }

        #endregion // Public properties

        #region Public methods

        /// <summary>
        /// Start tag timer and enabling creation of tags via command
        /// </summary>
        public void Start()
        {
            if (!Enabled)
            {
                Enabled = true;
                _timer = new DispatcherTimer(new TimeSpan(0, 0, 1), DispatcherPriority.Normal, _timer_Tick, _dispatcher);
                // timer seems to start by default
                //_timer.Start();
            }
        }

        /// <summary>
        /// Stop tag timer and disable further tag creation via command
        /// </summary>
        public void Stop()
        {
            if (Enabled)
            {
                Enabled = false;
                _timer.Stop();
            }
        }

        /// <summary>
        /// Add a clip tag with an explictly provided timestamp to the tag controllers
        /// clip tag collection. Provided as a public method in additio to AddTagCommand
        /// to allow later addition of tags after recording
        /// </summary>
        /// <param name="tagTime"> Timestamp to generate tag with </param>
        /// <param name="group"> ID of tag group to add tag to </param>
        public void AddTag(int tagTime, int group)
        {
            int startTime = tagTime - _clipLength / 2;
            startTime = startTime < 0 ? 0 : startTime;
            int endTime = startTime + _clipLength;
            _tags.Add(new ClipTag(startTime, endTime, group));
        }

        /// <summary>
        /// Add event handler on button press of buzz device which will add a tag if tag controller is enabled
        /// and change lights
        /// </summary>
        /// <param name="buzzDevice"></param>
        public void HookUpBuzzDevice(BuzzHandsetDevice buzzDevice)
        {
            if (buzzDevice != null)
                buzzDevice.OnButtonChanged += buzzDevice_OnButtonChanged;
        }

        #endregion // Public methods

        #region Commands
               
        #region AddTag

        private RelayCommand _addTagCommand;

        /// <summary>
        /// Create a new ClipTag object with timestamp corresponding to the current controller internal elapsed time timer
        /// and tag group ID specified by the command parameter
        /// </summary>
        public ICommand AddTagCommand
        {
            get
            {
                if (_addTagCommand == null)
                {
                    _addTagCommand = new RelayCommand
                    (
                        param => this.AddTag(param),
                        param => this.CanAddTag(param)
                    );
                }
                return _addTagCommand;
            }
        }

        private void AddTag(object param)
        {
            int groupID = (int)param;
            AddTag(ElapsedTime, groupID);
        }

        private bool CanAddTag(object param)
        {
            return _tags != null && Enabled && param is int && (int)param >= 0;
        }

        #endregion // AddTag  
                
        #endregion // Commands
    }

}
